/**
 * 服务器推送信息
 * @param globalSettings
 * @constructor
 */
function Doufugan(globalSettings) {

    /**
     * Sets the value inside the object
     * Use case:
     *     A fact that in plain js: A[b.c] !== A.b.c;
     *     this function can correct that by treating
     *     the string "b.c" as an array
     * @private
     */
    _setInner = function (t, str, o) {
        var strArr = str.split(/\./);
        if ((typeof t) !== 'object') {
            throw new Error("First input must be an object!");
        }
        var n = t;
        for (var i = 0; i < strArr.length; i = i + 1) {
            if (i == (strArr.length - 1)) {
                n[strArr[i]] = o;
            }
            n = n[strArr[i]] ? n[strArr[i]] :
                n[strArr[i]] = {};
        }
        console.log('setInner result', t);
        return o;
    };

    var t = this;
    globalSettings = globalSettings || {};

    var defaults = {
        systemRoot: "localhost:5617",
        symbol_id: "id",
        // localhost:8080/api/sync?
        symbol_page_data: "rows",
        symbol_page_size: "total",
        symbol_record_size: "records",

        symbol_rows_in_each_page: "rows",
        symbol_current_page: "page",


        /**
         *  {
         *      user: {
         *          avatar:
         *          uid:[...]
         *          name:[...]
          *          ....
          *     },
         *      login_time: '[...]'
         *      login_ip: '[...]'
         *  }
         */


        Sync: {
            url: "/api/sync",
            nextTick: 1000,
            symbol_msg_type: '_type',
            symbol_msg_alive: 'alive',
            symbol_msg_control: 'control',
            symbol_function: '_function',
            symbol_controller: '_controller',
            symbol_appId: '_aid',
            symbol_sessionId: '_sid',
            symbol_arguments: '_args',

            hooks_renderNormalMsg: [function (msg) {
                LOG("NMSG", msg)
            }],
            hooks_renderErrorlMsg: [function (msg) {
                LOG("NMSG", msg)
            }],
            hook_doControl: function (ctrl) {
                LOG("NMSG", ctrl)
            },
            hook_getNgController: function (controller) {
                return undefined;
            }
        }
    };

    //local
    var settings = mergeOptions({}, defaults, globalSettings);
    var _sync = new Sync();

    //Exports
    //-----------------------------------------------------------
    /**
     * Getting options
     */
    t.options = settings;

    t.Sync = _sync;

    t.LOG = LOG;
    t.SET_POPUP_ONCLOSE = SET_POPUP_ONCLOSE;
    t.POPUP_SETTING = POPUP_SETTING;
    t.POPUP_CLOSE = POPUP_CLOSE;
    t.extend = mergeOptions;

    t.config = config;

    //Libs (components)
    t.$LOG = LOG;
    t.$HTTP = new HTTP();
    t._setInner = _setInner;
    t._getInner = function (o,key) {
        if (typeof (o) !== 'object') {
            throw new Error('Please use object as first argument');
        }
         var ks = key.split(/\./);
         var t=o;
         for(var i=0;i<ks.length;i++){
            t=t[ks[i]];
         }
        return t;
    };


    //Exports END ------

    //Components

    function config() {
        if (arguments.length == 0) {
            return this.options;
        }
        if (arguments.length == 1) {
            return this.options[arguments[0]];
        }
    };

    function Sync() {
        var t = this;
        //exports
        t.sync = sync;

        var setting = settings.Sync;

        function pushNormalMessage(msg) {
            $.each(setting.hook)
            if ($.isFunction(setting.hooks_renderNormalMsg)) {
                setting.hooks_renderNormalMsg(msg);
            }
        }

        function pushErrorMessage(msg) {
            if ($.isFunction(setting.hook_renderErrorMsg)) {
                setting.hook_renderErrorMsg(msg);
            }
        }


        function sync() {
            GET(settings.Sync.url, {
                nextTick: settings.Sync.nextTick
            }, function (json) {
                var type = json[setting.symbol_msg_type];
                //control msg -- to call a controller function
                if (setting.symbol_msg_control == type) {
                    //var controllerName = json[setting.symbol_controller];
                    //var funcName = json[setting.symbol_function];
                    //var args = json[setting.symbol_arguments];
                    //var controller = setting.hook_getNgController(controllerName);
                    //if (controller && (typeof(controller) == 'object')) {
                    //    var func = controller[funcName];
                    //    if (func && (typeof(func) == 'function')) {
                    //        func(args);
                    //    }
                    //    else {
                    //        LOG("Sync error", "" + funcName + " not found");
                    //    }
                    //}
                    //else {
                    //    LOG("Sync error", "" + controllerName + " not found");
                    //}
                    setting.hook_doControl(ctrl);
                }
                //normal
                else {
                    pushNormalMessage(json);
                }
                //sync
                sync();
            }, function (errMsg) {
                console.log("Sync failed:" + errMsg);
                sync();
            });
        }
    }


    //UTIL


// Recursively combines option hash-objects.
// Better than `$.extend(true, ...)` because arrays are not traversed/copied.
//
// called like:
// mergeOptions(target, obj1, obj2, ...)
//
    function mergeOptions(target) {
        function mergeIntoTarget(name, value) {
            if ($.isPlainObject(value) && $.isPlainObject(target[name])
            /*&& !isForcedAtomicOption(name)*/) {
                // merge into a new object to avoid destruction
                target[name] = mergeOptions({}, target[name], value); // combine. `value` object takes precedence
            }
            else if (value !== undefined) { // only use values that are set and not undefined
                target[name] = value;
            }
        }

        for (var i = 1; i < arguments.length; i++) {
            $.each(arguments[i], mergeIntoTarget);
        }
        return target;
    }

    function LOG() {
        if (window.console && window.console.log) {
            window.console.log(arguments);
        }
    }


    var POPUP_ONCLOSE_FUNCTION_LABEL = "uu12hjkjksd";

    function SET_POPUP_ONCLOSE(f) {
        window.document[POPUP_ONCLOSE_FUNCTION_LABEL] = f;
    }

    /**
     * default settings string for window.open
     * @param option
     * @constructor
     * @return {string}
     */
    function POPUP_SETTING(option) {
        var height = 400;
        var width = 500;
        var top = 200;
        var left = 200;
        if (option && (typeof option) === 'object') {
            height = option.height || height;
            width = option.width || width;
            top = option.top || top;
            left = option.left || left;
        }
        return "scrollbars=yes, resizable=yes," +
            "menubar=no,titlebar=no,location=no,toolbar=no," +
            "top=" + top + ", left=" + left + ", width=" + width + ", height=" + height + "";
    }

    var POPUP_CLOSE = function (t) {
        (window.opener.document[POPUP_ONCLOSE_FUNCTION_LABEL]
        || function () {
            console.log('t', t);
            window.close();
//            alert("on close!");
        })(t);
    };

    function HTTP() {
        /**
         * post方法 返回数据为json
         *
         * @param url
         *            地址
         * @param data
         *            数据
         * @param cb
         *            回调函数
         */
        function POST(url, data, cbs, cbe) {
            $.ajax({
                url: url,
                data: data,
                type: 'post',
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    console.log(XMLHttpRequest);
                    if (cbe && typeof(cbe) === 'function')
                        cbe(XMLHttpRequest.responseText, textStatus, XMLHttpRequest);
                    else
                        alert(XMLHttpRequest.responseText);
                },
                success: cbs
            });
        }

        /**
         * get方法 返回数据为json
         *
         * @param url
         *            地址
         * @param data
         *            数据
         * @param cb
         *            回调函数
         */
        function GET(url, data, cbs, cbe) {
            $.ajax({
                url: url,
                data: data,
                type: 'get',
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    if (cbe && typeof(cbe) === 'function')
                        cbe(XMLHttpRequest.responseText, textStatus, XMLHttpRequest);
                },
                success: cbs
            });
        }

        /**
         * put方法 返回数据为json
         *
         * @param url
         *            地址
         * @param data
         *            数据
         * @param cb
         *            回调函数
         */
        function PUT(url, data, cbs, cbe) {
            $.ajax({
                url: url,
                data: data,
                type: 'put',
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    if (cbe && typeof(cbe) === 'function')
                        cbe(XMLHttpRequest.responseText, textStatus, XMLHttpRequest);
                },
                success: cbs
            });
        }

        /**
         * delete方法 返回数据为json
         *
         * @param url
         *            地址
         * @param data
         *            数据
         * @param cb
         *            回调函数
         * @returns
         */
        function DEL(url, data, cbs, cbe) {
            var _url = url;
            if (typeof(data) === 'object') {
                _url += '?';
                for (var i in data) {
                    _url += i + '=' + data[i] + '&'
                }
            }
            else _url += ('?' + data);
            $.ajax({
                url: url,
                type: 'delete',
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    if (cbe && typeof(cbe) === 'function')
                        cbe(XMLHttpRequest.responseText, textStatus, XMLHttpRequest);
                },
                success: cbs
            });
        }

        this.get = GET;
        this.post = POST;
        this.del = DEL;
        this.put = PUT;

        this.GET = GET;
        this.POST = POST;
        this.DEL = DEL;
        this.PUT = PUT;
    }

    t.commonService = new CommonService();
    function CommonService() {
        this.curd = function (_proto) {
            var proto = t.extend({}, _proto);
            var $http = t.$HTTP;
            var options = t.options;
//        var cfg = dfg.options[proto];

            if (proto === undefined) {
                throw new Error("init with right proto:" + proto);
            }
            var url = proto.url;
            delete proto.url;
            var $LOG = t.$LOG;
            return {
                get: get,
                del: del,
                add: add,
                edit: edit,
                list: list,
                raw: raw,
                wrap: buildByProto
            };

            function buildByProto(input) {
                var field;
                var ret = {};
                for (var f in proto) {
                    field = proto[f];
                    ret[f] = input[field];
                }
                ret = dfg.extend({}, input, ret);
                return ret;
            }

            /**
             * CallBack take the "Page" object,
             * queried by using the params.
             * Something like these (not exactly the same but depends on the dfg configuration :)
             * {
         *  rows:[//data array],
         *  pgLen:
         *  page:
         *  total:
         * }
             * @param queryObj
             * @param cbs
             * @param cbe
             */
            function raw(queryObj, cbs, cbe) {
                $http.get(url, queryObj,
                    (cbs || function (obj) {
                        $LOG("list succeed", obj);
                    }),
                    (cbe || function (e) {
                        $LOG("list failed", e);
                    })
                );
            }

            /**
             * CallBack take the array
             * @param queryObj
             * @param cbs
             * @param cbe
             */
            function list(queryObj, cbs, cbe) {
                $http.get(url, queryObj,
                    (function (resp) {
                        //var rows = resp[options.Common.symbol_page_data];
                        var rows = resp['rows'];
                        var list = [];
                        $.each(rows, function (i, elem) {
                            list.push(buildByProto(elem))
                        });
                        cbs(list);
                    } || function (s) {
                        $LOG("get succeed", s);
                    }),
                    (cbe || function (e) {
                        $LOG("list failed", e);
                    })
                );
            }

            /**
             * CallBack take the "Entity" object,
             * queried by using the params and retrieves the first(or nothing).
             * @param queryObj
             * @param cbs
             * @param cbe
             */
            function get(queryObj, cbs, cbe) {
                if (queryObj) {
                    $http.get(url, queryObj,
                        (function (resp) {
                            var rows = resp['rows'];
                            if (rows && rows[0]) {
                                cbs(buildByProto(rows[0]));
                            }
                            else {
                                cbs(undefined);
                            }
                        } || function (s) {
                            $LOG("get succeed", s);
                        }),
                        (cbe || function (e) {
                            $LOG("get failed", e);
                        })
                    );
                } else {
                    $LOG("invalid id", id);
                }
            }

            function edit(obj, cbs, cbe) {
                var id = obj[proto.id];
                if (id) {
                    $http.put(url + '/' + id, obj,
                        (cbs || function () {
                            $LOG("edit succeed", obj);
                        }),
                        (cbe || function (e) {
                            $LOG("edit failed", obj, e);
                        })
                    );
                } else {
                    $LOG("invalid id", id);
                }
            }

            function add(obj, cbs, cbe) {
                $http.post(url, obj,
                    (cbs || function () {
                        $LOG("add succeed", obj);
                    }),
                    (cbe || function (e) {
                        $LOG("add failed", obj, e);
                    })
                );
            }

            function del(obj, cbs, cbe) {
                var id = obj[proto.id];
                if (id) {
                    $http.del(url + '/' + id, {},
                        (cbs || function () {
                            $LOG("delete succeed", obj);
                        }),
                        (cbe || function (e) {
                            $LOG("delete failed", obj, e);
                        })
                    );
                } else {
                    $LOG("invalid id", id);
                }
            }
        }
    }
}

